﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Data.SqlClient;
using System.Collections;
using System.Data;
using ICarPool.ORM.Common;
using ICarPool.ORM.DBUtility;
using ICarPool.ORM.Attributes;


namespace ICarPool.ORM.EntityManager
{
    internal class EntityManagerImpl : IEntityManager
    {
        
        private const string ID_CONST = @"ID";

        #region Transaction 注入事物对象属性
        IDbTransaction transaction = null;

        public IDbTransaction Transaction
        {
            get { return transaction; }
            set { transaction = value; }
        }
        #endregion


        #region IEntityManager Members



        public int Save<T>(T entity)
        {
            if (entity == null)
            {
                return 0;
            }

            TableInfo tableInfo = DbEntityUtils.GetTableInfo(entity, DbOperateType.INSERT);

            //由于ID是自增长，不需要设置ID参数，所以参数数目减一
            IDbDataParameter[] parms = DbFactory.CreateDbParameters(tableInfo.Columns.Count);

            DbEntityUtils.SetParameters(tableInfo.Columns, parms);

            object val = AdoHelper.ExecuteNonQuery(transaction, CommandType.Text, tableInfo.InsertSQL, parms);

            return Convert.ToInt32(val);
        }

        public int Update<T>(T entity) 
        {
            if (entity == null)
            {
                return 0;
            }

            TableInfo tableInfo = DbEntityUtils.GetTableInfo(entity, DbOperateType.UPDATE);
            IDbDataParameter[] parms = DbFactory.CreateDbParameters(tableInfo.Columns.Count);
            DbEntityUtils.SetParameters(tableInfo.Columns, parms);
            object val = AdoHelper.ExecuteNonQuery(transaction, CommandType.Text, tableInfo.UpdateSQL, parms);

            return Convert.ToInt32(val);
        }

        public int Remove<T>(T entity) 
        {
            if (entity == null)
            {
                return 0;
            }

            TableInfo tableInfo = DbEntityUtils.GetTableInfo(entity, DbOperateType.DELETE);

            IDbDataParameter[] parms = DbFactory.CreateDbParameters(1);
            parms[0].ParameterName = ID_CONST;
            parms[0].Value = tableInfo.Columns[ID_CONST];
            object val = AdoHelper.ExecuteNonQuery(transaction, CommandType.Text, tableInfo.DeleteSQL, parms);

            return Convert.ToInt32(val);
        }

        public int Remove<T>(object id) where T : new()
        {
            if (id == null)
            {
                return 0;
            }

            TableInfo tableInfo = DbEntityUtils.GetTableInfo(typeof(T));
            IDbDataParameter[] parms = DbFactory.CreateDbParameters(1);
            parms[0].ParameterName = ID_CONST;
            parms[0].Value = id;
            object val = AdoHelper.ExecuteNonQuery(transaction, CommandType.Text, tableInfo.DeleteSQL, parms);

            return Convert.ToInt32(val);
        }

        public int RemoveByCondition<T>(string condition) where T : new()
        {
            string strSql = string.Format(@" DELETE FROM {0} WHERE {1} ", DbEntityUtils.GetTableName(typeof(T)), condition); 
 
            object val = AdoHelper.ExecuteNonQuery(transaction, CommandType.Text,strSql );

            return Convert.ToInt32(val);
        }

        public List<T> FindAll<T>() where T : new()
        {
            List<T> listArr = new List<T>();
            PropertyInfo[] properties = ReflectionUtils.GetProperties(typeof(T));
            TableInfo tableInfo = DbEntityUtils.GetTableInfo(typeof(T));

            IDataReader sdr = null;
            try
            {
                sdr = AdoHelper.ExecuteReader(AdoHelper.ConnectionString, CommandType.Text, tableInfo.FindAllSQL);
                while (sdr.Read())
                {
                    T entity = new T();
                    foreach (PropertyInfo property in properties)
                    {
                        //若从数据库中取出该属性，则赋值
                        if (sdr.ContainColumn(property.Name))
                        {
                            ReflectionUtils.SetPropertyValue(entity, property, sdr[property.Name]);
                        }
                    }

                    listArr.Add(entity);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (sdr != null) sdr.Close();
            }

            return listArr;
        }

        public List<T> FindAll<T>(int pageIndex, int pageSize, out int totalCount, string orderBy) where T : new()
        {
            List<T> listArr = new List<T>();
            PropertyInfo[] properties = ReflectionUtils.GetProperties(typeof(T));
            TableInfo tableInfo = DbEntityUtils.GetTableInfo(typeof(T));
            IDbConnection conn = DbFactory.CreateDbConnection(AdoHelper.ConnectionString);
            conn.Open();
            IDataReader sdr = null;
            try
            {
                object count = AdoHelper.ExecuteScalar(conn, CommandType.Text, string.Format(@"SELECT COUNT(ID) FROM {0}", tableInfo.TableName));
                totalCount = count == null ? 0 : (int)count;

                string sqlSelect = string.Format(@"SELECT TOP {0} {1} FROM (SELECT ROW_NUMBER() OVER(ORDER BY {2} ) as POS,{1} FROM {3}) T 
                                                   WHERE POS BETWEEN {4} AND {5}",pageSize, tableInfo.AllProperty,orderBy, tableInfo.TableName,
                                                                                 pageIndex * pageSize, (pageIndex+1) * pageSize);
                sdr = AdoHelper.ExecuteReader(conn, CommandType.Text, sqlSelect);
                while (sdr.Read())
                {
                    T entity = new T();
                    foreach (PropertyInfo property in properties)
                    {
                        //若从数据库中取出该属性，则赋值
                        if (sdr.ContainColumn(property.Name))
                        {
                            ReflectionUtils.SetPropertyValue(entity, property, sdr[property.Name]);
                        }
                    }

                    listArr.Add(entity);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (sdr != null) sdr.Close();
                if (conn != null) conn.Close();
            }

            return listArr;
        }

        public List<T> FindBySql<T>(string strSql) where T : new()
        {
            List<T> listArr = new List<T>();
            PropertyInfo[] properties = ReflectionUtils.GetProperties(typeof(T));
            TableInfo tableInfo = DbEntityUtils.GetTableInfo(typeof(T));

            IDataReader sdr = null;
            try
            {
                sdr = AdoHelper.ExecuteReader(AdoHelper.ConnectionString, CommandType.Text, strSql);
                while (sdr.Read())
                {
                    T entity = new T();

                    foreach (PropertyInfo property in properties)
                    {
                        if (sdr.ContainColumn(property.Name))
                        {
                            ReflectionUtils.SetPropertyValue(entity, property, sdr[property.Name]);
                        }
                    }

                    listArr.Add(entity);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (sdr != null) sdr.Close();
            }

            return listArr;
        }

        public List<T> FindBySql<T>(string strSql, int pageIndex, int pageSize, out int totalCount, string orderBy) where T : new()
        {
            List<T> listArr = new List<T>();
            PropertyInfo[] properties = ReflectionUtils.GetProperties(typeof(T));
            TableInfo tableInfo = DbEntityUtils.GetTableInfo(typeof(T));
            IDbConnection conn = DbFactory.CreateDbConnection(AdoHelper.ConnectionString);
            IDataReader sdr = null;
            conn.Open();
            try
            {
                object count = AdoHelper.ExecuteScalar(conn, CommandType.Text, strSql);
                totalCount = count == null ? 0 : (int)count;
                strSql = strSql.Insert(strSql.IndexOf(" "), string.Format(@" ROW_NUMBER() OVER(ORDER BY {0} ) as POS, ", orderBy));
                string sqlSelect = string.Format(@"SELECT TOP {0} * FROM ({1}) T WHERE POS BETWEEN {2} AND {3}", pageSize, strSql,
                                                                                 pageIndex * pageSize, (pageIndex + 1) * pageSize);
                sdr = AdoHelper.ExecuteReader(conn, CommandType.Text, sqlSelect);
                while (sdr.Read())
                {
                    T entity = new T();

                    foreach (PropertyInfo property in properties)
                    {
                        if (sdr.ContainColumn(property.Name))
                        {
                            ReflectionUtils.SetPropertyValue(entity, property, sdr[property.Name]);
                        }
                    }

                    listArr.Add(entity);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (sdr != null) sdr.Close();
                if (conn != null) conn.Close();
            }

            return listArr;
        }

        public T FindById<T>(object id) where T : new()
        {
            T entity =  new T();
            PropertyInfo[] properties = ReflectionUtils.GetProperties(typeof(T));
            TableInfo tableInfo = DbEntityUtils.GetTableInfo(typeof(T));
            IDbDataParameter[] parms = DbFactory.CreateDbParameters(1);
            parms[0].ParameterName = ID_CONST;
            parms[0].Value = id;

            IDataReader sdr = null;
            try
            {
                sdr = AdoHelper.ExecuteReader(AdoHelper.ConnectionString, CommandType.Text, tableInfo.FindByIDSQL, parms);
                if (sdr.Read())
                {
                    foreach (PropertyInfo property in properties)
                    {
                        if (sdr.ContainColumn(property.Name))
                        {
                            ReflectionUtils.SetPropertyValue(entity, property, sdr[property.Name]);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (sdr != null) sdr.Close();
            }

            return entity;
        }

        public List<T> FindByCondition<T>(string condition) where T : new()
        {
            List<T> listArr = new List<T>();
            PropertyInfo[] properties = ReflectionUtils.GetProperties(typeof(T));
            TableInfo tableInfo = DbEntityUtils.GetTableInfo(typeof(T));

            IDataReader sdr = null;
            try
            {
                sdr = AdoHelper.ExecuteReader(AdoHelper.ConnectionString, CommandType.Text, tableInfo.FindAllProSQL + condition);
                while (sdr.Read())
                {
                    T entity = new T();
                    foreach (PropertyInfo property in properties)
                    {
                        if (sdr.ContainColumn(property.Name))
                        {
                            ReflectionUtils.SetPropertyValue(entity, property, sdr[property.Name]);
                        }
                    }

                    listArr.Add(entity);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (sdr != null) sdr.Close();
            }

            return listArr;
        }

        public List<T> FindByCondition<T>(string condition, int pageIndex, int pageSize, out int totalCount, string orderBy) where T : new()
        {
            List<T> listArr = new List<T>();
            PropertyInfo[] properties = ReflectionUtils.GetProperties(typeof(T));
            TableInfo tableInfo = DbEntityUtils.GetTableInfo(typeof(T));
            IDbConnection conn = DbFactory.CreateDbConnection(AdoHelper.ConnectionString);
            conn.Open();
            IDataReader sdr = null;
            try
            {
                object count = AdoHelper.ExecuteScalar(conn, CommandType.Text, tableInfo.FindAllProSQL + condition);
                totalCount = count == null ? 0 : (int)count;

                string sqlSelect = string.Format(@"SELECT TOP {0} {1} FROM (SELECT ROW_NUMBER() OVER(ORDER BY {2} ) as POS,{1} FROM {3} WHERE {6}) T 
                                                   WHERE POS BETWEEN {4} AND {5}", pageSize, tableInfo.AllProperty, orderBy, tableInfo.TableName,
                                                                                 pageIndex * pageSize, (pageIndex + 1) * pageSize,condition);
                sdr = AdoHelper.ExecuteReader(conn, CommandType.Text, sqlSelect);
                while (sdr.Read())
                {
                    T entity = new T();
                    foreach (PropertyInfo property in properties)
                    {
                        if (sdr.ContainColumn(property.Name))
                        {
                            ReflectionUtils.SetPropertyValue(entity, property, sdr[property.Name]);
                        }
                    }

                    listArr.Add(entity);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (sdr != null) sdr.Close();
                if (conn != null) conn.Close();
            }

            return listArr;
        }

        #endregion
    }

    public static class Extension
    {
        public static bool ContainColumn(this IDataReader reader, string columnName)
        {
            try
            {
                return reader.GetOrdinal(columnName) >= 0;
            }
            catch (Exception)
            {
                return false;
            }
        }
    }
}
